package com.ruding.disruptor.utils.http;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

/**
 * http客户端
 * @author 520475
 *
 */
public class HttpClient {
    
    private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
    
    public int timeout = 60000;
    
    public static HttpClient build(HttpConfig config) {
        HttpClient httpClient = new HttpClient();
        return httpClient;
    }
    
    public HttpClient setTimeout(int timeout) {
        this.timeout = timeout;
        return this;
    }

    public HttpResult get(String url) throws Exception {
        return get(url, null, null);
    }

    public HttpResult get(String url, Properties header) throws Exception {
        return get(url, header, null);
    }
    
    public HttpResult get(String url, Map<String, Object> params) throws Exception {
        return get(url, null, params);
    }

    public HttpResult get(String url, Properties header, Map<String, Object> params) throws Exception {
        
        if(params != null){
            String paramStr = "";
            for (String key : params.keySet()) {
                paramStr += (key + "=" + params.get(key) + "&");
            }
            if (paramStr.lastIndexOf("&") > 0) {
                paramStr = paramStr.substring(0, paramStr.length() - 1);
            }
            url += ("?" + paramStr);
        }
        
        HttpURLConnection http = (HttpURLConnection) new URL(url).openConnection();
        http.setConnectTimeout(timeout);
        http.setReadTimeout(timeout);
        http.setDoInput(true);
        http.setDoOutput(true);
        http.setRequestMethod("GET");
        if(header != null) {
            Enumeration<Object> keys = header.keys();
            while(keys.hasMoreElements()){
                String key = (String) keys.nextElement();
                http.setRequestProperty(key, header.getProperty(key));
            }
        }
        http.setInstanceFollowRedirects(false);
        http.connect();
        
        HttpResult httpResult = new HttpResult();
        BufferedInputStream bin = null;
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        try {
            int statusCode = http.getResponseCode();
            if(statusCode == 200) {
                bin = new BufferedInputStream(http.getInputStream());
            }else if(statusCode == 302) {
                String redirect = http.getHeaderField("Location");
                LOG.info("reqeust info [GET]: redirect={}, code={}", redirect, statusCode);
                return get(redirect, header, params);
            }else {
                LOG.info("reqeust error [GET]: url={}, code={}", url, statusCode);
                bin = new BufferedInputStream(http.getErrorStream());
            }
            byte[] buffer = new byte[1024 * 2];
            int len = 0;
            while((len = bin.read(buffer)) != -1) {
                bout.write(buffer, 0, len);
            }
            bout.flush();
            httpResult.setStatusCode(statusCode);
            httpResult.setBody(bout.toByteArray());
            httpResult.setHeaders(http.getHeaderFields());
        } catch (Exception e) {
            LOG.error("reqeust error [GET]: url={}, messgae={}", url, e.getMessage(), e);
            throw e;
        } finally {
            try {
               if(bin != null) {
                   bin.close();
               }
               if(bout != null) {
                   bout.close();
               }
            } catch (Exception e2) {
                LOG.error("reqeust error [GET]: url={}, messgae={}", e2);
            }
        }
        
        return httpResult;
    }

    public HttpResult post(String url) throws Exception {
        return post(url, null, new HashMap<>());
    }
    
    public HttpResult post(String url, Properties header) throws Exception {
        return post(url, header, new HashMap<>());
    }
    
    public HttpResult post(String url, Map<String, Object> params) throws Exception {
        return post(url, null, params);
    }

    public HttpResult post(String url, Properties header, Map<String, Object> params) throws Exception {
        HttpURLConnection http = (HttpURLConnection) new URL(url).openConnection();
        http.setConnectTimeout(timeout);
        http.setReadTimeout(timeout);
        http.setDoInput(true);
        http.setDoOutput(true);
        http.setRequestMethod("POST");
        if(header != null) {
            Enumeration<Object> keys = header.keys();
            while(keys.hasMoreElements()){
                String key = (String) keys.nextElement();
                http.setRequestProperty(key, header.getProperty(key));
            }
        }
        http.connect();
        
        String paramStr = "";
        if(params != null){
            for (String key : params.keySet()) {
                paramStr += (key + "=" + params.get(key) + "&");
            }
            if (paramStr.lastIndexOf("&") > 0) {
                paramStr = paramStr.substring(0, paramStr.length() - 1);
            }
        }
        
        HttpResult httpResult = new HttpResult();
        PrintWriter writer = null;
        BufferedInputStream bin = null;
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        try {
            writer = new PrintWriter(http.getOutputStream());
            writer.write(paramStr);
            writer.flush();
            
            int statusCode = http.getResponseCode();
            if(statusCode == 200) {
                bin = new BufferedInputStream(http.getInputStream());
            }else if(statusCode == 302) {
                String redirect = http.getHeaderField("Location");
                LOG.info("reqeust info [GET]: redirect={}, code={}", redirect, statusCode);
                return post(redirect, header, params);
            }else {
                LOG.info("reqeust error [GET]: url={}, code={}", url, statusCode);
                bin = new BufferedInputStream(http.getErrorStream());
            }
            byte[] buffer = new byte[1024 * 2];
            int len = 0;
            while((len = bin.read(buffer)) != -1) {
                bout.write(buffer, 0, len);
            }
            bout.flush();
            httpResult.setStatusCode(statusCode);
            httpResult.setBody(bout.toByteArray());
            httpResult.setHeaders(http.getHeaderFields());
        } catch (Exception e) {
            LOG.error("reqeust error [GET]: url={}, messgae={}", url, e.getMessage(), e);
            throw e;
        } finally {
            try {
               if(bin != null) {
                   bin.close();
               }
               if(bout != null) {
                   bout.close();
               }
            } catch (Exception e2) {
                LOG.error("reqeust error [GET]: url={}, messgae={}", e2);
            }
        }
        
        return httpResult;
    }
    
    
    public HttpResult post(String url, JSONObject json) throws Exception {
        return post(url, null, json);
    }
    
    public HttpResult post(String url, Properties header, JSONObject json) throws Exception {
        HttpURLConnection http = (HttpURLConnection) new URL(url).openConnection();
        http.setConnectTimeout(timeout);
        http.setReadTimeout(timeout);
        http.setDoInput(true);
        http.setDoOutput(true);
        http.setRequestMethod("POST");
        if(header != null) {
            Enumeration<Object> keys = header.keys();
            while(keys.hasMoreElements()){
                String key = (String) keys.nextElement();
                http.setRequestProperty(key, header.getProperty(key));
            }
        }
        http.setRequestProperty("Content-Type","application/json; charset=UTF-8");
        http.setRequestProperty("Accept","application/json");
        http.connect();
        
        HttpResult httpResult = new HttpResult();
        PrintWriter writer = null;
        BufferedInputStream bin = null;
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        try {
            writer = new PrintWriter(http.getOutputStream());
            writer.write(json.toJSONString());
            writer.flush();
            
            int statusCode = http.getResponseCode();
            if(statusCode == 200) {
                bin = new BufferedInputStream(http.getInputStream());
            }else if(statusCode == 302) {
                String redirect = http.getHeaderField("Location");
                LOG.info("reqeust info [GET]: redirect={}, code={}", redirect, statusCode);
                return post(redirect, header, json);
            }else {
                LOG.info("reqeust error [GET]: url={}, code={}", url, statusCode);
                bin = new BufferedInputStream(http.getErrorStream());
            }
            byte[] buffer = new byte[1024 * 2];
            int len = 0;
            while((len = bin.read(buffer)) != -1) {
                bout.write(buffer, 0, len);
            }
            bout.flush();
            httpResult.setStatusCode(statusCode);
            httpResult.setBody(bout.toByteArray());
            httpResult.setHeaders(http.getHeaderFields());
        } catch (Exception e) {
            LOG.error("reqeust error [GET]: url={}, messgae={}", url, e.getMessage(), e);
            throw e;
        } finally {
            try {
               if(bin != null) {
                   bin.close();
               }
               if(bout != null) {
                   bout.close();
               }
            } catch (Exception e2) {
                LOG.error("reqeust error [GET]: url={}, messgae={}", e2);
            }
        }
        
        return httpResult;
    }

}
